#pragma once
#include <string>
#include "../PocelainMacro.h"
#include "../PocelainType.h"
#include "../core/Ref.h"
#include "EUniformType.h"
POCELAIN_NAMESPACE_BEGIN
template<class T>
static EUniformType GetUniformType()
{
    // Not Support Type 
    POCELAIN_ASSERT(false);
    return EUniformType::E_UNIFORM_UNKNOWN;
}
template<>
EUniformType GetUniformType<bool>()
{
    return EUniformType::E_UNIFORM_BOOL;
}
template<>
EUniformType GetUniformType<int8_t>()
{
    return EUniformType::E_UNIFORM_BYTE;
}
template<>
EUniformType GetUniformType<uint8_t>()
{
    return EUniformType::E_UNIFORM_UBYTE;
}
template<>
EUniformType GetUniformType<int16_t>()
{
    return EUniformType::E_UNIFORM_SHORT;
}
template<>
EUniformType GetUniformType<uint16_t>()
{
    return EUniformType::E_UNIFORM_USHORT;
}
template<>
EUniformType GetUniformType<int32_t>()
{
    return EUniformType::E_UNIFORM_INT;
}
template<>
EUniformType GetUniformType<uint32_t>()
{
    return EUniformType::E_UNIFORM_UINT;
}
template<>
EUniformType GetUniformType<float>()
{
    return EUniformType::E_UNIFORM_FLOAT;
}
template<>
EUniformType GetUniformType<double>()
{
    return EUniformType::E_UNIFORM_DOUBLE;
}
template<>
EUniformType GetUniformType<TVector<float,2>>()
{
    return EUniformType::E_UNIFORM_VECTOR2;
}
template<>
EUniformType GetUniformType<TVector<float,3>>()
{
    return EUniformType::E_UNIFORM_VECTOR3;
}
template<>
EUniformType GetUniformType<TVector<float,4>>()
{
    return EUniformType::E_UNIFORM_VECTOR4;
}
template<>
EUniformType GetUniformType<TVector<double,2>>()
{
    return EUniformType::E_UNIFORM_DVECTOR2;
}
template<>
EUniformType GetUniformType<TVector<double,3>>()
{
    return EUniformType::E_UNIFORM_DVECTOR3;
}
template<>
EUniformType GetUniformType<TVector<double,4>>()
{
    return EUniformType::E_UNIFORM_DVECTOR4;
}
template<>
EUniformType GetUniformType<TVector<bool,2>>()
{
    return EUniformType::E_UNIFORM_BVECTOR2;
}
template<>
EUniformType GetUniformType<TVector<bool,3>>()
{
    return EUniformType::E_UNIFORM_BVECTOR3;
}
template<>
EUniformType GetUniformType<TVector<bool,4>>()
{
    return EUniformType::E_UNIFORM_BVECTOR4;
}
template<>
EUniformType GetUniformType<TMatrix<float,2,2>>()
{
    return EUniformType::E_UNIFORM_MATRIX2x2;
}
template<>
EUniformType GetUniformType<TMatrix<float,2,3>>()
{
    return EUniformType::E_UNIFORM_MATRIX2x3;
}
template<>
EUniformType GetUniformType<TMatrix<float,2,4>>()
{
    return EUniformType::E_UNIFORM_MATRIX2x4;
}
template<>
EUniformType GetUniformType<TMatrix<float,3,2>>()
{
    return EUniformType::E_UNIFORM_MATRIX3x2;
}
template<>
EUniformType GetUniformType<TMatrix<float,3,3>>()
{
    return EUniformType::E_UNIFORM_MATRIX3x3;
}
template<>
EUniformType GetUniformType<TMatrix<float,3,4>>()
{
    return EUniformType::E_UNIFORM_MATRIX3x4;
}
template<>
EUniformType GetUniformType<TMatrix<float,4,2>>()
{
    return EUniformType::E_UNIFORM_MATRIX4x2;
}
template<>
EUniformType GetUniformType<TMatrix<float,4,3>>()
{
    return EUniformType::E_UNIFORM_MATRIX4x3;
}
template<>
EUniformType GetUniformType<TMatrix<float,4,4>>()
{
    return EUniformType::E_UNIFORM_MATRIX4x4;
}
template<>
EUniformType GetUniformType<TMatrix<double,2,2>>()
{
    return EUniformType::E_UNIFORM_DMATRIX2x2;
}
template<>
EUniformType GetUniformType<TMatrix<double,2,3>>()
{
    return EUniformType::E_UNIFORM_DMATRIX2x3;
}
template<>
EUniformType GetUniformType<TMatrix<double,2,4>>()
{
    return EUniformType::E_UNIFORM_DMATRIX2x4;
}
template<>
EUniformType GetUniformType<TMatrix<double,3,2>>()
{
    return EUniformType::E_UNIFORM_DMATRIX3x2;
}
template<>
EUniformType GetUniformType<TMatrix<double,3,3>>()
{
    return EUniformType::E_UNIFORM_DMATRIX3x3;
}
template<>
EUniformType GetUniformType<TMatrix<double,3,4>>()
{
    return EUniformType::E_UNIFORM_DMATRIX3x4;
}
template<>
EUniformType GetUniformType<TMatrix<double,4,2>>()
{
    return EUniformType::E_UNIFORM_DMATRIX4x2;
}
template<>
EUniformType GetUniformType<TMatrix<double,4,3>>()
{
    return EUniformType::E_UNIFORM_DMATRIX4x3;
}
template<>
EUniformType GetUniformType<TMatrix<double,4,4>>()
{
    return EUniformType::E_UNIFORM_DMATRIX4x4;
}

class IUniform: public Ref
{
public:
    IUniform(){};
    virtual ~IUniform(){};
    virtual IUniform *clone()const = 0;
    virtual EUniformType getUniformType()const
    {
        return EUniformType::E_UNIFORM_UNKNOWN;
    }
};

template<class T>
class Uniform: public IUniform
{
public:
    typedef Uniform<T> type;
    Uniform(){};
    Uniform(const type &other)
    {
        mValue = other.mValue;
    }
    Uniform(type &&other)
    {
        mValue = std::move(other.mValue);
    }
    Uniform(const T &val)
    {
        mValue = val;
    }
    virtual ~Uniform(){};
    void setValue(const T &val)
    {
        mValue = val;
    }
    T &getValue()
    {
        return mValue;
    }
    type &operator=(const type &other)
    {
        mValue = other.mValue;
    }
    type &operator=(type &&other)
    {
        mValue = std::move(other.mValue);
    }
    virtual EUniformType getUniformType()const override
    {
        return GetUniformType<T>();
    }
    virtual IUniform *clone()const override
    {
        return CreateRef<type>(*this);
    }
private:
    T mValue;
};
POCELAIN_NAMESPACE_END
